#include "wx/wxprec.h"

#ifdef __BORLANDC__
#pragma hdrstop
#endif

#ifndef WX_PRECOMP
    #include <wx/wx.h>
#endif

#include <wx/dcbuffer.h>
#include "wx/wxledpanel.h"

#define TIMER_SCROLL_ID 1000

BEGIN_EVENT_TABLE(wxLEDPanel, wxControl)
    EVT_PAINT(wxLEDPanel::OnPaint)
    EVT_ERASE_BACKGROUND(wxLEDPanel::OnEraseBackground)
    EVT_TIMER(TIMER_SCROLL_ID,wxLEDPanel::OnScrollTimer)
END_EVENT_TABLE()

wxLEDPanel::wxLEDPanel() :
    m_align(wxALIGN_LEFT|wxALIGN_TOP),
    m_padLeft(1),
    m_padRight(1),
    m_invert(false),
    m_show_inactivs(true),
    m_scrollspeed(0),
    m_scrolldirection(wxALL),
    m_aniFrameNr(-1) {
}

wxLEDPanel::wxLEDPanel(wxWindow* parent, wxWindowID id, const wxSize& ledsize,
                    const wxSize& fieldsize, int padding, const wxPoint& pos,
                    long style, const wxValidator& validator) :
    m_align(wxALIGN_LEFT|wxALIGN_TOP),
    m_padLeft(1),
    m_padRight(1),
    m_invert(false),
    m_show_inactivs(true),
    m_scrollspeed(0),
    m_scrolldirection(wxALL),
    m_aniFrameNr(-1) {
    Create(parent,id,ledsize,fieldsize,padding,pos,style,validator);
}

wxLEDPanel::~wxLEDPanel() {
}

bool wxLEDPanel::Create(wxWindow* parent, wxWindowID id, const wxSize& ledsize,
                    const wxSize& fieldsize, int padding, const wxPoint& pos,
                    long style, const wxValidator& validator) {
   
    m_ledsize=ledsize;
    m_padding=padding;
    wxSize size;
    size.SetWidth((ledsize.GetWidth()+padding)*fieldsize.GetWidth()+padding);
    size.SetHeight((ledsize.GetHeight()+padding)*fieldsize.GetHeight()+padding);

   
    if(!wxControl::Create(parent,id,pos,size,style,validator))
        return false;

   
    m_field.Init(0,fieldsize.GetWidth(),fieldsize.GetHeight());

   
    wxWindow::SetBackgroundColour(*wxBLACK);

   
    this->SetLEDColour(wxLED_COLOUR_RED);

   
    this->Enable(false);

   
    m_scrollTimer.SetOwner(this,TIMER_SCROLL_ID);

    return true;
}

wxSize wxLEDPanel::DoGetBestSize() const {
    wxSize size;
    size.SetWidth((m_ledsize.GetWidth()+m_padding)*m_field.GetWidth()+m_padding);
    size.SetHeight((m_ledsize.GetHeight()+m_padding)*m_field.GetHeight()+m_padding);
    return size;
}

void wxLEDPanel::Clear() {
    m_field.Clear();
}

void wxLEDPanel::Reset() {
    SetText(m_text);
}


wxSize wxLEDPanel::GetFieldsize() const {
    return m_field.GetSize();
}


wxSize wxLEDPanel::GetLEDSize() const {
    return m_ledsize;
}


void wxLEDPanel::SetLEDColour(wxLEDColour colourID) {
   
    wxBrush brush;
    wxPen pen;

   
    m_activ_colour_id=colourID;

    int w=m_ledsize.GetWidth()+m_padding;
    int h=m_ledsize.GetHeight()+m_padding;

   
    wxBitmap led_on(w,h);
    wxBitmap led_off(w,h);
    wxBitmap led_none(w,h);

   
    m_mdc_led_on.SelectObject(led_on);

   
    m_mdc_led_on.SetBackground(this->GetBackgroundColour());
    m_mdc_led_on.Clear();

   
    pen.SetColour(s_colour_dark[colourID-1]);
    brush.SetColour(s_colour[colourID-1]);
    m_mdc_led_on.SetPen(pen);
    m_mdc_led_on.SetBrush(brush);
    m_mdc_led_on.DrawEllipse(wxPoint(0,0),m_ledsize);

   
    pen.SetColour(s_colour_light[colourID-1]);
    m_mdc_led_on.SetPen(pen);
    m_mdc_led_on.DrawEllipticArc(0,0,m_ledsize.GetWidth(),m_ledsize.GetHeight(),75.0,195.0);


   
    m_mdc_led_off.SelectObject(led_off);

   
    m_mdc_led_off.SetBackground(this->GetBackgroundColour());
    m_mdc_led_off.Clear();

   
    pen.SetColour(s_colour_dark[colourID-1]);
    brush.SetColour(s_colour_verydark[colourID-1]);
    m_mdc_led_off.SetPen(pen);
    m_mdc_led_off.SetBrush(brush);
    m_mdc_led_off.DrawEllipse(wxPoint(0,0),m_ledsize);


   
    m_mdc_led_none.SelectObject(led_none);
    m_mdc_led_none.SetBackground(this->GetBackgroundColour());
    m_mdc_led_none.Clear();


    PrepareBackground();
}


const wxColour& wxLEDPanel::GetLEDColour() const {
    return s_colour[m_activ_colour_id];
}


bool wxLEDPanel::SetBackgroundColour(const wxColour& colour) {
    if (wxWindow::SetBackgroundColour(colour))
    {
      PrepareBackground();
      return true;
    }

    return false;
}


void wxLEDPanel::SetScrollSpeed(int speed) {
   
    m_scrollTimer.Stop();

   
    m_scrollspeed=speed;

   
    if(m_scrollspeed>0 && m_scrolldirection!=wxALL)
        m_scrollTimer.Start(speed,true);
}


int wxLEDPanel::GetScrollSpeed() const {
    return m_scrollspeed;
}


void wxLEDPanel::SetScrollDirection(wxDirection d) {
   
    m_scrollTimer.Stop();

   
    m_scrolldirection=d;

    if(m_scrollspeed>0 && m_scrolldirection!=wxALL)
        m_scrollTimer.Start(m_scrollspeed,true);
}


wxDirection wxLEDPanel::GetScrollDirection() const {
    return m_scrolldirection;
}


void wxLEDPanel::ShowInvertet(bool invert) {
    if(m_invert==invert) return;

    m_invert=invert;
    PrepareBackground();
}


void wxLEDPanel::ShowInactivLEDs(bool show_inactivs) {
    if(m_show_inactivs==show_inactivs) return;

    m_show_inactivs=show_inactivs;
    PrepareBackground();
}

void wxLEDPanel::SetContentAlign(int a) {
   
    m_align=a;

   
    ResetPos();

   
    m_field.Clear();
    m_field.SetDatesAt(m_pos,m_content_mo);
}

int wxLEDPanel::GetContentAlign() const {
    return m_align;
}

void wxLEDPanel::SetText(const wxString& text, int align) {
   
    if(text.IsEmpty()) return;

   
    MatrixObject* tmp=NULL;

   
    if(align!=-1) m_align=align;

   
    m_text=text;
    m_aniFrameNr=-1;

   
    if(m_align&wxALIGN_CENTER_HORIZONTAL)
        tmp=m_font.GetMOForText(text,wxALIGN_CENTER_HORIZONTAL);
    else if(m_align&wxALIGN_RIGHT)
        tmp=m_font.GetMOForText(text,wxALIGN_RIGHT);
    else tmp=m_font.GetMOForText(text);   

   
    m_content_mo.Init(*tmp);
    delete tmp;

   
    ResetPos();

   
    m_field.Clear();
    m_field.SetDatesAt(m_pos,m_content_mo);
}


wxString wxLEDPanel::GetText() const {
    return m_text;
}

void wxLEDPanel::SetImage(const wxImage img) {
    if(!img.IsOk()) return;
    m_text.Empty();

    m_content_mo.Init(img);
    m_aniFrameNr=-1;

   
    ResetPos();

   
    m_field.Clear();
    m_field.SetDatesAt(m_pos,m_content_mo);
}

wxImage wxLEDPanel::GetContentAsImage() const {
    return m_content_mo.GetAsImage();
}

void wxLEDPanel::SetAnimation(const wxAnimation ani) {
    if(!ani.IsOk() || ani.GetFrameCount()==0) return;

    m_ani = ani;
    m_text.Empty();
    m_aniFrameNr = 0;

    m_content_mo.Init(ani.GetFrame(0));

   
    ResetPos();

   
    m_field.Clear();
    m_field.SetDatesAt(m_pos,m_content_mo);

   
    m_scrollTimer.Stop();
    m_scrollspeed = m_ani.GetDelay(0);
    m_scrollTimer.Start(m_scrollspeed,true);
}

const wxAnimation wxLEDPanel::GetAnimation() const {
    return m_ani;
}

void wxLEDPanel::SetContentPaddingLeft(int padLeft) {
   
    m_padLeft=padLeft;

   
    ResetPos();

   
    m_field.Clear();
    m_field.SetDatesAt(m_pos,m_content_mo);
}

int wxLEDPanel::GetContentPaddingLeft() const {
    return m_padLeft;
}

void wxLEDPanel::SetContentPaddingRight(int padRight) {
   
    m_padRight=padRight;

   
    ResetPos();

   
    m_field.Clear();
    m_field.SetDatesAt(m_pos,m_content_mo);
}

int wxLEDPanel::GetContentPaddingRight() const {
    return m_padRight;
}


void wxLEDPanel::SetLetterSpace(int letterSpace) {
   
    if(m_font.GetLetterSpace()==letterSpace) return;

    m_font.SetLetterSpace(letterSpace);
    Reset();
}


int wxLEDPanel::GetLetterSpace() const {
    return m_font.GetLetterSpace();
}

void wxLEDPanel::SetFontType(wxLEDFontType t) {
    if(m_font.GetFontType()==t) return;

    m_font.SetFontType(t);
    Reset();
}

wxLEDFontType wxLEDPanel::GetFontType() const {
    return m_font.GetFontType();
}


void wxLEDPanel::DrawField(wxDC& dc, bool backgroundMode) {
    wxPoint point;
    int w=m_ledsize.GetWidth()+m_padding;
    int h=m_ledsize.GetHeight()+m_padding;

   
    int x=0,y=0;

   
    wxMemoryDC* p_mdc_data=((m_invert)?((m_show_inactivs)?(&m_mdc_led_off):(&m_mdc_led_none)):(&m_mdc_led_on));
    wxMemoryDC* p_mdc_nodata=((m_invert)?(&m_mdc_led_on):((m_show_inactivs)?(&m_mdc_led_off):(&m_mdc_led_none)));

    int l = m_field.GetLength();
    int fw = m_field.GetWidth();
    const char* field = m_field.GetData();
    for(int i=0;i<l;++i)
    {
       
        point.x=x*w+m_padding;
        point.y=y*h+m_padding;

       
        if(field[i] && !backgroundMode)
        {
            dc.Blit(point.x,point.y,w,h,p_mdc_data,0,0);
        }
        else if(backgroundMode)
        {
            dc.Blit(point.x,point.y,w,h,p_mdc_nodata,0,0);
        }

       
        ++x;
        if(x==fw) {++y; x=0;}
    }
}


void wxLEDPanel::OnEraseBackground(wxEraseEvent& event) {
    (void)event;
}

void wxLEDPanel::OnPaint(wxPaintEvent &event) {
    (void)event;
    wxBufferedPaintDC dc(this);
   
   

   
    dc.Blit(0,0,m_mdc_background.GetSize().GetWidth(),m_mdc_background.GetSize().GetHeight(),&m_mdc_background,0,0);
   
    DrawField(dc);
}

void wxLEDPanel::ShiftLeft() {
   
    m_pos.x--;

   
    if(m_pos.x+m_content_mo.GetWidth()<=0)
    {
        m_pos.x=m_field.GetWidth();
        return;
    }

   
    m_field.ShiftLeft();

   
   
    for(int i=0;i<m_content_mo.GetHeight();++i)
    {
        char d=m_content_mo.GetDataFrom(abs(m_pos.x-m_field.GetWidth()+1),i);
        if(d>0) m_field.SetDataAt(m_field.GetWidth()-1,m_pos.y+i,d);
    }
}

void wxLEDPanel::ShiftRight() {
   
    m_pos.x++;
   
    if(m_pos.x>=m_field.GetWidth())
    {
        m_pos.x=-m_content_mo.GetWidth();   
        return;
    }

   
    m_field.ShiftRight();

   
   
   
    for(int i=0;i<m_content_mo.GetHeight();++i)
    {
        char d=m_content_mo.GetDataFrom(abs(m_pos.x-m_field.GetWidth()+1),i);
        if(d>0) m_field.SetDataAt(0,m_pos.y+i,d);
    }
}

void wxLEDPanel::ShiftUp() {
   
    m_pos.y--;
   
    if(m_pos.y+m_content_mo.GetHeight()<=0)
        m_pos.y=m_field.GetHeight();

   
    m_field.Clear();
    m_field.SetDatesAt(m_pos,m_content_mo);
}

void wxLEDPanel::ShiftDown() {
   
    m_pos.y++;
   
    if(m_pos.y>=m_field.GetHeight())
        m_pos.y=-m_content_mo.GetHeight();

   
    m_field.Clear();
    m_field.SetDatesAt(m_pos,m_content_mo);

}

void wxLEDPanel::OnScrollTimer(wxTimerEvent& event) {
    (void)event;
    if(m_scrollspeed==0||m_content_mo.IsEmpty()) return;

   
    m_scrollTimer.Stop();

    if(m_aniFrameNr < 0)
    {

       
        switch(m_scrolldirection)
        {
            case wxALL: return;
            case wxLEFT: this->ShiftLeft(); break;
            case wxRIGHT: this->ShiftRight(); break;
            case wxDOWN: this->ShiftDown(); break;
            case wxUP: this->ShiftUp(); break;
            default: return;
        }
    }
    else
    {
        m_aniFrameNr++;
        if(m_aniFrameNr >= m_ani.GetFrameCount())
            m_aniFrameNr=0;

        m_content_mo.Init(m_ani.GetFrame(m_aniFrameNr));
        m_field.Clear();
        m_field.SetDatesAt(m_pos,m_content_mo);
        m_scrollspeed = m_ani.GetDelay(m_aniFrameNr);
    }

   
    this->Refresh();

   
    m_scrollTimer.Start(m_scrollspeed,true);
}


void wxLEDPanel::ResetPos() {
   
    if(m_content_mo.GetData()==NULL) return;

   
    if(m_scrolldirection!=wxLEFT && m_scrolldirection!=wxRIGHT)
    {
        if(m_align & wxALIGN_RIGHT)
            m_pos.x=m_field.GetWidth()-m_content_mo.GetWidth()-m_padRight;
        else if(m_align & wxALIGN_CENTER_HORIZONTAL)
            m_pos.x=(m_field.GetWidth()-m_content_mo.GetWidth())/2;
        else
            m_pos.x=m_padLeft;
    }
    else if(m_scrolldirection==wxLEFT)
        m_pos.x=m_field.GetWidth();
    else if(m_scrolldirection==wxRIGHT)
        m_pos.x=-m_content_mo.GetWidth();

   
    if(m_scrolldirection!=wxUP && m_scrolldirection!=wxDOWN)
    {
        if(m_align & wxALIGN_BOTTOM)
            m_pos.y=m_field.GetHeight()-m_content_mo.GetHeight();
        else if(m_align & wxALIGN_CENTER_VERTICAL)
            m_pos.y=(m_field.GetHeight()-m_content_mo.GetHeight())/2;
        else
            m_pos.y=0;
    }
    else if(m_scrolldirection==wxUP)
        m_pos.y=m_field.GetHeight();
    else if(m_scrolldirection==wxDOWN)
        m_pos.y=-m_content_mo.GetHeight();
}


void wxLEDPanel::PrepareBackground() {
    wxSize s=DoGetBestSize();
    wxBitmap bmpBG(s.GetWidth(),s.GetHeight());

    m_mdc_background.SelectObject(bmpBG);

   
    m_mdc_background.SetBackground(this->GetBackgroundColour());
    m_mdc_background.Clear();

    if(m_invert || m_show_inactivs)
        DrawField(m_mdc_background, true);
}

const wxColour wxLEDPanel::s_colour[7]=
    {    wxColour(255,0,0), wxColour(0,255,0), wxColour(0,0,255),
        wxColour(255,255,0), wxColour(255,0,255), wxColour(0,255,255),
        wxColour(128,128,128) };

const wxColour wxLEDPanel::s_colour_dark[7]=
    {    wxColour(128,0,0), wxColour(0,128,0), wxColour(0,0,128),
        wxColour(128,128,0), wxColour(128,0,128), wxColour(0,128,128),
        wxColour(64,64,64) };

const wxColour wxLEDPanel::s_colour_verydark[7]=
    {    wxColour(64,0,0), wxColour(0,64,0), wxColour(0,0,64),
        wxColour(64,64,0), wxColour(64,0,64), wxColour(0,64,64),
        wxColour(32,32,32) };

const wxColour wxLEDPanel::s_colour_light[7]=
    {    wxColour(255,128,128), wxColour(128,255,128), wxColour(128,128,255),
        wxColour(255,255,128), wxColour(255,128,255), wxColour(128,255,255),
        wxColour(192,192,192) };
